#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <pthread.h>
#include <unistd.h>
#include<cassert>

const int defaultnum=5;

class ThreadInfo
{
public:
    std::string threadname;
    pthread_t tid;
};

template <class T>
class ThreadPool
{
private:
    //有关锁和条件变量的函数的封装
    void Lock()
    {
        pthread_mutex_lock(&mutex_);
    }

    void UnLock()
    {
        pthread_mutex_unlock(&mutex_);
    }

    void Wakeup()
    {
        pthread_cond_signal(&cond_);      
    }

    void ThreadSleep()
    {
        pthread_cond_wait(&cond_, &mutex_);     //唤醒的两个条件：有锁，有人唤醒我(有人调用pthread_cond_signal函数)
    }

    std::string GetThreadName(pthread_t tid)
    {
        for(const auto& ti:threads_)
        {
            if(ti.tid==tid)
            {
                return ti.threadname;
            }
        }
        return "None";
    }

    bool  IsTaskEmpty()
    {
        return tasks_.empty();
    }


public:
    static void* HandlerTask(void* args)
    {
        ThreadPool<T>* tp=static_cast<ThreadPool<T>*>(args);
        pthread_t tid=pthread_self();
        std::string threadname=tp->GetThreadName(tid);
        pthread_detach(tid);
        //开始执行任务

        while(true)
        {
            tp->Lock();

            while (tp->IsTaskEmpty())
            {
                tp->ThreadSleep(); // 默认为阻塞，while防止虚假唤醒  //唤醒的两个条件：有锁，有push任务
            }

            T t = tp->PopTask();

            tp->UnLock();

            t();
        }
       
        return nullptr;
    }

    void PushTask(const T& t)
    {
        Lock();
        tasks_.push(t);
        Wakeup();     //有任务了，唤醒线程执行任务
        UnLock();
    }

    T PopTask()    //PopTask不需要加锁，因为在HandlerTask内，PopTask在加锁区域内，并且也不能加锁，加了会导致死锁
    {
        assert(pthread_mutex_trylock(&mutex_) != 0);
        T t=tasks_.front();
        tasks_.pop();
        return t;
    }

    static ThreadPool<T>* GetInstance()   //目的：得到 ThreadPool对象，new一个
    {
        if(nullptr==tp_)
        {
            pthread_mutex_lock(&lock_);  //为什么加锁，防止多个线程执行单例模式函数
            std::cout << "log: singleton create done first!" << std::endl;   //单例模式(singleton)的创建已完成
            tp_=new ThreadPool<T>();
            pthread_mutex_unlock(&lock_);
        }
        return tp_;
    }

    void Start()
    {
        int nums=threads_.size();
        for(int i=0;i<nums;i++)
        {
            //讲线程的信息也放入了threads数组中
            threads_[i].threadname=std::to_string(i)+"_Thread";
            pthread_create(&threads_[i].tid,nullptr,HandlerTask,this);
        }
    }

    void Destroy()
    {
       if(tp_)
       {
            delete tp_;   //调用析构函数,不可以直接~ThreadPool();
            tp_ = nullptr;
       }
    }

private:
    ThreadPool(int num = defaultnum) : threads_(num)
    {
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&cond_, nullptr);
    }

    ~ThreadPool()     //一般含单例模式的类的析构函数都是private，因为防止外部销毁
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cond_);
    }

    ThreadPool(const ThreadPool<T> &) = delete;   //拷贝构造
    const ThreadPool<T> &operator=(const ThreadPool<T> &) = delete; // a=b，拷贝赋值

private:
    std::vector<ThreadInfo> threads_;    //放线程
    std::queue<T> tasks_;               //放任务
    static ThreadPool<T>* tp_;       //配合单例模式，且必须要用static
    static pthread_mutex_t lock_;    //只搭配单例模式的锁，使用两把锁，使代码清晰

    pthread_mutex_t mutex_;       
    pthread_cond_t cond_;
};

template <class T>
ThreadPool<T>* ThreadPool<T>::tp_=nullptr;   //tp_的初始化,必须加ThreadPool<T>::

//为什么必须要设置为static，而不能是非static,并在Start中初始化，因为ThreadPool<Task>::GetInstance()->Start();  GetInstance在Start前面
template <class T>
pthread_mutex_t ThreadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER;  //lock_的初始化